﻿using System;
using System.Collections.Concurrent;
using UMC.Data;
using System.Text;
using System.Net.Sockets;

namespace UMC.Proxy
{

    class LogSetting : UMC.Data.DataProvider
    {

        UdpClient client;
        class UdpWriter : System.IO.TextWriter
        {
            byte[] _buffers;
            short _bufferSize, _seq = 0;
            UdpClient client;
            int hashcode;
            public override Encoding Encoding => Encoding.UTF8;
            public UdpWriter(UdpClient client)
            {
                this.client = client;
                this.hashcode = Guid.NewGuid().GetHashCode();
                _buffers = System.Buffers.ArrayPool<Byte>.Shared.Rent(548);
            }

            public override void Flush()
            {

                BitConverter.TryWriteBytes(_buffers, hashcode);
                BitConverter.TryWriteBytes(_buffers.AsSpan(4), -_seq);
                _seq = 0;
                BitConverter.TryWriteBytes(_buffers.AsSpan(6), _bufferSize);
                this.client.Send(_buffers, _bufferSize + 8);
                _bufferSize = 0;
                this.hashcode = Guid.NewGuid().GetHashCode();

            }
            public override void Close()
            {
                base.Dispose(true);
            }
            protected override void Dispose(bool disposing)
            {
                if (_buffers != null)
                {
                    System.Buffers.ArrayPool<Byte>.Shared.Return(_buffers);
                }
                _buffers = null;

                base.Dispose(disposing);
            }
            char[] _chars = new char[1];

            void Send()
            {
                BitConverter.TryWriteBytes(_buffers, hashcode);
                BitConverter.TryWriteBytes(_buffers.AsSpan(4), _seq);
                _seq++;
                BitConverter.TryWriteBytes(_buffers.AsSpan(6), _bufferSize);
                this.client.Send(_buffers, _bufferSize + 8);
                _bufferSize = 0;
            }
            public override void Write(char value)
            {
                if (_bufferSize + 6 > 540)
                {
                    Send();
                    _bufferSize = 0;
                }
                _chars[0] = value;
                _bufferSize += Convert.ToInt16(Encoding.UTF8.GetBytes(_chars, 0, 1, _buffers, _bufferSize + 8));

            }
        }

        public static LogSetting Instance()
        {
            if (_Instance == null)
            {
                _Instance = new LogSetting();
                _Instance.LoadConf();
            }
            return _Instance;
        }

        static LogSetting _Instance;

        bool _isWriter = false;
        public virtual bool IsWriter
        {
            get
            {
                return _isWriter;
            }
        }
        public void LoadConf()
        {
            var provider = Reflection.Configuration("assembly")?["Log"];
            if (provider != null)
            {
                var host = provider["host"];
                if (String.IsNullOrEmpty(host) == false)
                {
                    var port = Utility.IntParse(provider["port"], 5188);
                    _isWriter = true;
                    this.client = new UdpClient(host, port);
                }
                else
                {
                    _isWriter = false;
                }
            }
            else
            {
                _isWriter = false;
            }
            foreach (var l in _logMetas)
            {
                l.Dispose();
            }
            _logMetas.Clear();

        }


        ConcurrentQueue<UdpWriter> _logMetas = new ConcurrentQueue<UdpWriter>();

        public virtual void Write(Web.WebMeta logMeta)
        {
            if (this._isWriter)
            {
                UdpWriter writer;
                if (_logMetas.TryDequeue(out writer))
                {
                }
                else
                {
                    writer = new UdpWriter(this.client);

                } 
                UMC.Data.JSON.Serialize(logMeta, writer);
                writer.Flush();
                _logMetas.Enqueue(writer);
            }
        }
        public static void Instance(LogSetting logSetting, Provider provider)
        {
            logSetting.Provider = provider;
            _Instance = logSetting;
        }
    }
}

